package com.wqnmdb.im.netty.dispose.impl;

import com.alibaba.fastjson.JSONObject;
import com.wqnmdb.im.domain.contants.ConstantEnum;
import com.wqnmdb.im.domain.mongo.ImMessage;
import com.wqnmdb.im.domain.mongo.ImUser;
import com.wqnmdb.im.domain.netty.contants.NettyIdentifier;
import com.wqnmdb.im.netty.data.NettyData;
import com.wqnmdb.im.domain.netty.protobuf.Message;
import com.wqnmdb.im.domain.netty.protobuf.NettyModel;
import com.wqnmdb.im.netty.dispose.MsgDispose;
import com.wqnmdb.im.netty.dispose.NettyDispose;
import com.wqnmdb.im.service.MsgService;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class NettyDisposeImpl extends NettyData implements NettyDispose {

    @Autowired
    private AsynAndCacheService asynAndCacheService;

    @Autowired
    private MsgDispose msgDispose;


    /**
     * 开启定时任务,xx秒后执行一次
     */
    @Override
    public void addChannel(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        channel.eventLoop().schedule(() -> {
            //如果channel 没有登录就关掉channel
            if (!channel.hasAttr(NettyIdentifier.AUTH_KEY) || !channel.attr(NettyIdentifier.AUTH_KEY).get()) {
                log.info("通道：{}.超出认证时间，通道关闭", channel.id());
                channel.close();
            }
        }, ConstantEnum.NETTY_AUTH_TIMEOUT.getCode(), TimeUnit.SECONDS);
    }

    /**
     * 绑定用户
     */
    @Override
    public void bindingUserChannel(ImUser user, String appName, ChannelHandlerContext ctx) {
        //绑定通道
        ChannelId channelId = ctx.channel().id();
        ConcurrentHashMap<ChannelId, ChannelHandlerContext> contextChannel = SYS_CONTEXT_CHANNEL.get(appName);

        if (contextChannel.containsKey(channelId)) {
            log.debug("客户端:{}.是连接状态", channelId);
        } else {
            InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
            String clientIp = insocket.getAddress().getHostAddress();
            int clientPort = insocket.getPort();
            //保存连接
            contextChannel.put(channelId, ctx);
            log.info("客户端:{}.连接netty服务器IP:{}.端口:{}.", channelId, clientIp, clientPort);
            log.info("当前通道数量:{}.", contextChannel.size());
        }

        ConcurrentHashMap<String, ChannelId> userChannel = APP_BINDING_CHANNEL.get(appName);
        String userKey = user.getUserId().toString();
        if (userChannel.containsKey(userKey)) {
            log.debug("客户端:{}.已绑定用户:{}.", channelId, userKey);
        } else {
            userChannel.put(userKey, channelId);
            log.info("客户端:{}.绑定用户:{}.所属APP：{}.", channelId, userKey, appName);
        }

        //app在线人数及用户在线状态
        asynAndCacheService.statAppAndUser(appName, user.getUserId(), 1);
        log.info("APP：{}.用户：{}.绑定完成.", appName, userKey);
    }

    /**
     * 删除连接
     */
    @Override
    public void remove(ChannelHandlerContext ctx) {
        log.info("删除通道及缓存：{}.", ctx.channel().id());
        ctx.channel().attr(NettyIdentifier.CLEAR_KEY).set(Boolean.TRUE);
        ChannelId channelId = ctx.channel().id();

        //删除上下文通道
        SYS_CONTEXT_CHANNEL.forEach((appName, context) -> {
            if (context.containsKey(channelId)) {
                //删除绑定
                ChannelHandlerContext delCtx = context.get(channelId);
                if (delCtx.channel().hasAttr(NettyIdentifier.USER_ID_KEY)) {
                    Integer userId = delCtx.channel().attr(NettyIdentifier.USER_ID_KEY).get();
                    if (userId != null && userId > 0) {
                        String userKey = userId.toString();
                        asynAndCacheService.delCache(userId, appName);
                        asynAndCacheService.statAppAndUser(appName, userId, 2);
                        //删除当前用户绑定通道
                        ConcurrentHashMap<String, ChannelId> bindingChannel = APP_BINDING_CHANNEL.get(appName);
                        if (bindingChannel.containsKey(userKey)) {
                            bindingChannel.remove(userKey);
                            log.info("删除成功,当前APP:{}.通道绑定数量:{}.", appName, bindingChannel.size());
                        }
                    }
                }
                context.remove(channelId);
                log.info("删除成功,当前APP:{}.连接通道数量:{}.", appName, context.size());
                return;
            }
        });

        //关闭通道
        ctx.close();
    }

    /**
     * 处理消息
     */
    @Override
    public void receiveMsg(ChannelHandlerContext ctx, Object msg) {
        log.info("接收通道:{}.消息:{}.", ctx.channel().id(), msg);
        NettyModel.ReqModel reqModel = (NettyModel.ReqModel) msg;
        Integer userId = ctx.channel().attr(NettyIdentifier.USER_ID_KEY).get();


        ImUser user = asynAndCacheService.getUser(
                userId, asynAndCacheService.getUserCollectionName(ctx.channel().attr(NettyIdentifier.APP_NAME).get()));

        switch (reqModel.getCmd()) {
            case 96:
                msgDispose.createSession(ctx, reqModel, user);
                break;
            case 2:
                msgDispose.singleSend(ctx, reqModel, user);
                break;
            case 3:
                msgDispose.affirm(ctx, reqModel, user);
                break;
            case 4:
                msgDispose.recall(ctx, reqModel, user);
                break;
            default:
                msgDispose.msgError(ctx, reqModel);
                break;
        }
    }
}